home *** CD-ROM | disk | FTP | other *** search
/ Download Now 8 / Download Now V8.iso / Program / InternetTools / ApacheWebServer1.3.6 / apache_1_3_6_win32.exe / _SETUP.1 / ab.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-02-19  |  30.2 KB  |  1,010 lines

  1. /* ====================================================================
  2.  * Copyright (c) 1998-1999 The Apache Group.  All rights reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions
  6.  * are met:
  7.  *
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  *
  11.  * 2. Redistributions in binary form must reproduce the above copyright
  12.  *    notice, this list of conditions and the following disclaimer in
  13.  *    the documentation and/or other materials provided with the
  14.  *    distribution.
  15.  *
  16.  * 3. All advertising materials mentioning features or use of this
  17.  *    software must display the following acknowledgment:
  18.  *    "This product includes software developed by the Apache Group
  19.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  20.  *
  21.  * 4. The names "Apache Server" and "Apache Group" must not be used to
  22.  *    endorse or promote products derived from this software without
  23.  *    prior written permission. For written permission, please contact
  24.  *    apache@apache.org.
  25.  *
  26.  * 5. Products derived from this software may not be called "Apache"
  27.  *    nor may "Apache" appear in their names without prior written
  28.  *    permission of the Apache Group.
  29.  *
  30.  * 6. Redistributions of any form whatsoever must retain the following
  31.  *    acknowledgment:
  32.  *    "This product includes software developed by the Apache Group
  33.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  34.  *
  35.  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
  36.  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  37.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  38.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
  39.  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  41.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  42.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  43.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  44.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  45.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  46.  * OF THE POSSIBILITY OF SUCH DAMAGE.
  47.  * ====================================================================
  48.  *
  49.  * This software consists of voluntary contributions made by many
  50.  * individuals on behalf of the Apache Group and was originally based
  51.  * on public domain software written at the National Center for
  52.  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
  53.  * For more information on the Apache Group and the Apache HTTP server
  54.  * project, please see <http://www.apache.org/>.
  55.  *
  56.  */
  57.  
  58. /*
  59.    ** This program is based on ZeusBench V1.0 written by Adam Twiss
  60.    ** which is Copyright (c) 1996 by Zeus Technology Ltd. http://www.zeustech.net/
  61.    **
  62.    ** This software is provided "as is" and any express or implied waranties,
  63.    ** including but not limited to, the implied warranties of merchantability and
  64.    ** fitness for a particular purpose are disclaimed.  In no event shall
  65.    ** Zeus Technology Ltd. be liable for any direct, indirect, incidental, special,
  66.    ** exemplary, or consequential damaged (including, but not limited to,
  67.    ** procurement of substitute good or services; loss of use, data, or profits;
  68.    ** or business interruption) however caused and on theory of liability.  Whether
  69.    ** in contract, strict liability or tort (including negligence or otherwise)
  70.    ** arising in any way out of the use of this software, even if advised of the
  71.    ** possibility of such damage.
  72.    **
  73.  */
  74.  
  75. /*
  76.    ** HISTORY:
  77.    **    - Originally written by Adam Twiss <adam@zeus.co.uk>, March 1996
  78.    **      with input from Mike Belshe <mbelshe@netscape.com> and
  79.    **      Michael Campanella <campanella@stevms.enet.dec.com>
  80.    **    - Enhanced by Dean Gaudet <dgaudet@apache.org>, November 1997
  81.    **    - Cleaned up by Ralf S. Engelschall <rse@apache.org>, March 1998
  82.    **    - POST and verbosity by Kurt Sussman <kls@merlot.com>, August 1998
  83.    **    - HTML table output added by David N. Welton <davidw@prosa.it>, January 1999
  84.    **
  85.  */
  86.  
  87. /*
  88.  * BUGS:
  89.  *
  90.  * - uses strcpy/etc.
  91.  * - has various other poor buffer attacks related to the lazy parsing of
  92.  *   response headers from the server
  93.  * - doesn't implement much of HTTP/1.x, only accepts certain forms of
  94.  *   responses
  95.  * - (performance problem) heavy use of strstr shows up top in profile
  96.  *   only an issue for loopback usage
  97.  */
  98.  
  99. #define VERSION "1.3"
  100.  
  101. /*  -------------------------------------------------------------------- */
  102.  
  103. /* affects include files on Solaris */
  104. #define BSD_COMP
  105.  
  106. /* allow compilation outside an Apache build tree */
  107. #ifdef NO_APACHE_INCLUDES
  108. #include <sys/time.h>
  109. #include <sys/ioctl.h>
  110. #include <sys/stat.h>
  111. #include <unistd.h>
  112. #include <stdlib.h>
  113. #include <stdio.h>
  114. #include <fcntl.h>
  115. #include <sys/socket.h>
  116. #include <netinet/in.h>
  117. #include <netdb.h>
  118. #include <errno.h>
  119. #include <sys/ioctl.h>
  120. #include <string.h>
  121.  
  122. #define ap_select       select
  123. #else /* (!)NO_APACHE_INCLUDES */
  124. #include "ap_config.h"
  125. #include <fcntl.h>
  126. #include <sys/time.h>
  127. #endif /* NO_APACHE_INCLUDES */
  128. /* ------------------- DEFINITIONS -------------------------- */
  129.  
  130. /* maximum number of requests on a time limited test */
  131. #define MAX_REQUESTS 50000
  132.  
  133. /* good old state hostname */
  134. #define STATE_UNCONNECTED 0
  135. #define STATE_CONNECTING  1
  136. #define STATE_READ        2
  137.  
  138. #define CBUFFSIZE       512
  139.  
  140. struct connection {
  141.     int fd;
  142.     int state;
  143.     int read;            /* amount of bytes read */
  144.     int bread;            /* amount of body read */
  145.     int length;            /* Content-Length value used for keep-alive */
  146.     char cbuff[CBUFFSIZE];    /* a buffer to store server response header */
  147.     int cbx;            /* offset in cbuffer */
  148.     int keepalive;        /* non-zero if a keep-alive request */
  149.     int gotheader;        /* non-zero if we have the entire header in cbuff */
  150.     struct timeval start, connect, done;
  151. };
  152.  
  153. struct data {
  154.     int read;            /* number of bytes read */
  155.     int ctime;            /* time in ms to connect */
  156.     int time;            /* time in ms for connection */
  157. };
  158.  
  159. #define ap_min(a,b) ((a)<(b))?(a):(b)
  160. #define ap_max(a,b) ((a)>(b))?(a):(b)
  161.  
  162. /* --------------------- GLOBALS ---------------------------- */
  163.  
  164. int verbosity = 0;        /* no verbosity by default */
  165. int posting = 0;        /* GET by default */
  166. int requests = 1;        /* Number of requests to make */
  167. int concurrency = 1;        /* Number of multiple requests to make */
  168. int tlimit = 0;            /* time limit in cs */
  169. int keepalive = 0;        /* try and do keepalive connections */
  170. char servername[1024];        /* name that server reports */
  171. char hostname[1024];        /* host name */
  172. char path[1024];        /* path name */
  173. char postfile[1024];        /* name of file containing post data */
  174. char *postdata;            /* *buffer containing data from postfile */
  175. int postlen = 0;        /* length of data to be POSTed */
  176. char content_type[1024];    /* content type to put in POST header */
  177. int port = 80;            /* port number */
  178.  
  179. int use_html = 0;        /* use html in the report */
  180. char *tablestring;
  181. char *trstring;
  182. char *tdstring;
  183.  
  184. int doclen = 0;            /* the length the document should be */
  185. int totalread = 0;        /* total number of bytes read */
  186. int totalbread = 0;        /* totoal amount of entity body read */
  187. int totalposted = 0;        /* total number of bytes posted, inc. headers */
  188. int done = 0;            /* number of requests we have done */
  189. int doneka = 0;            /* number of keep alive connections done */
  190. int good = 0, bad = 0;        /* number of good and bad requests */
  191.  
  192. /* store error cases */
  193. int err_length = 0, err_conn = 0, err_except = 0;
  194. int err_response = 0;
  195.  
  196. struct timeval start, endtime;
  197.  
  198. /* global request (and its length) */
  199. char request[512];
  200. int reqlen;
  201.  
  202. /* one global throw-away buffer to read stuff into */
  203. char buffer[8192];
  204.  
  205. struct connection *con;        /* connection array */
  206. struct data *stats;        /* date for each request */
  207.  
  208. fd_set readbits, writebits;    /* bits for select */
  209. struct sockaddr_in server;    /* server addr structure */
  210.  
  211. /* --------------------------------------------------------- */
  212.  
  213. /* simple little function to perror and exit */
  214.  
  215. static void err(char *s)
  216. {
  217.     if (errno) {
  218.     perror(s);
  219.     }
  220.     else {
  221.     printf("%s", s);
  222.     }
  223.     exit(errno);
  224. }
  225.  
  226. /* --------------------------------------------------------- */
  227.  
  228. /* write out request to a connection - assumes we can write
  229.    (small) request out in one go into our new socket buffer  */
  230.  
  231. static void write_request(struct connection *c)
  232. {
  233.     gettimeofday(&c->connect, 0);
  234.     /* XXX: this could use writev for posting -- more efficient -djg */
  235.     write(c->fd, request, reqlen);
  236.     if (posting) {
  237.     write(c->fd, postdata, postlen);
  238.     totalposted += (reqlen + postlen);
  239.     }
  240.  
  241.     c->state = STATE_READ;
  242.     FD_SET(c->fd, &readbits);
  243.     FD_CLR(c->fd, &writebits);
  244. }
  245.  
  246. /* --------------------------------------------------------- */
  247.  
  248. /* make an fd non blocking */
  249.  
  250. static void nonblock(int fd)
  251. {
  252.     int i = 1;
  253.     ioctl(fd, FIONBIO, &i);
  254. }
  255.  
  256. /* --------------------------------------------------------- */
  257.  
  258. /* returns the time in ms between two timevals */
  259.  
  260. static int timedif(struct timeval a, struct timeval b)
  261. {
  262.     register int us, s;
  263.  
  264.     us = a.tv_usec - b.tv_usec;
  265.     us /= 1000;
  266.     s = a.tv_sec - b.tv_sec;
  267.     s *= 1000;
  268.     return s + us;
  269. }
  270.  
  271. /* --------------------------------------------------------- */
  272.  
  273. /* calculate and output results */
  274.  
  275. static void output_results(void)
  276. {
  277.     int timetaken;
  278.  
  279.     gettimeofday(&endtime, 0);
  280.     timetaken = timedif(endtime, start);
  281.  
  282.     printf("\r                                                                           \r");
  283.     printf("Server Software:        %s\n", servername);
  284.     printf("Server Hostname:        %s\n", hostname);
  285.     printf("Server Port:            %d\n", port);
  286.     printf("\n");
  287.     printf("Document Path:          %s\n", path);
  288.     printf("Document Length:        %d bytes\n", doclen);
  289.     printf("\n");
  290.     printf("Concurrency Level:      %d\n", concurrency);
  291.     printf("Time taken for tests:   %d.%03d seconds\n",
  292.        timetaken / 1000, timetaken % 1000);
  293.     printf("Complete requests:      %d\n", done);
  294.     printf("Failed requests:        %d\n", bad);
  295.     if (bad)
  296.     printf("   (Connect: %d, Length: %d, Exceptions: %d)\n",
  297.            err_conn, err_length, err_except);
  298.     if (err_response)
  299.     printf("Non-2xx responses:      %d\n", err_response);
  300.     if (keepalive)
  301.     printf("Keep-Alive requests:    %d\n", doneka);
  302.     printf("Total transferred:      %d bytes\n", totalread);
  303.     if (posting)
  304.     printf("Total POSTed:           %d\n", totalposted);
  305.     printf("HTML transferred:       %d bytes\n", totalbread);
  306.  
  307.     /* avoid divide by zero */
  308.     if (timetaken) {
  309.     printf("Requests per second:    %.2f\n", 1000 * (float) (done) / timetaken);
  310.     printf("Transfer rate:          %.2f kb/s received\n",
  311.            (float) (totalread) / timetaken);
  312.     if (posting) {
  313.         printf("                        %.2f kb/s sent\n",
  314.            (float) (totalposted) / timetaken);
  315.         printf("                        %.2f kb/s total\n",
  316.            (float) (totalread + totalposted) / timetaken);
  317.     }
  318.     }
  319.  
  320.     {
  321.     /* work out connection times */
  322.     int i;
  323.     int totalcon = 0, total = 0;
  324.     int mincon = 9999999, mintot = 999999;
  325.     int maxcon = 0, maxtot = 0;
  326.  
  327.     for (i = 0; i < requests; i++) {
  328.         struct data s = stats[i];
  329.         mincon = ap_min(mincon, s.ctime);
  330.         mintot = ap_min(mintot, s.time);
  331.         maxcon = ap_max(maxcon, s.ctime);
  332.         maxtot = ap_max(maxtot, s.time);
  333.         totalcon += s.ctime;
  334.         total += s.time;
  335.     }
  336.     printf("\nConnnection Times (ms)\n");
  337.     printf("              min   avg   max\n");
  338.     printf("Connect:    %5d %5d %5d\n", mincon, totalcon / requests, maxcon);
  339.     printf("Processing: %5d %5d %5d\n",
  340.            mintot - mincon, (total / requests) - (totalcon / requests),
  341.            maxtot - maxcon);
  342.     printf("Total:      %5d %5d %5d\n", mintot, total / requests, maxtot);
  343.     }
  344. }
  345.  
  346. /* --------------------------------------------------------- */
  347.  
  348. /* calculate and output results in HTML  */
  349.  
  350. static void output_html_results(void)
  351. {
  352.     int timetaken;
  353.  
  354.     gettimeofday(&endtime, 0);
  355.     timetaken = timedif(endtime, start);
  356.  
  357.     printf("\n\n<table %s>\n", tablestring);
  358.     printf("<tr %s><th colspan=2 %s>Server Software:</th>"
  359.        "<td colspan=2 %s>%s</td></tr>\n",
  360.        trstring, tdstring, tdstring, servername);
  361.     printf("<tr %s><th colspan=2 %s>Server Hostname:</th>"
  362.        "<td colspan=2 %s>%s</td></tr>\n",
  363.        trstring, tdstring, tdstring, hostname);
  364.     printf("<tr %s><th colspan=2 %s>Server Port:</th>"
  365.        "<td colspan=2 %s>%d</td></tr>\n",
  366.        trstring, tdstring, tdstring, port);
  367.     printf("<tr %s><th colspan=2 %s>Document Path:</th>"
  368.        "<td colspan=2 %s>%s</td></tr>\n",
  369.        trstring, tdstring, tdstring, path);
  370.     printf("<tr %s><th colspan=2 %s>Document Length:</th>"
  371.        "<td colspan=2 %s>%d bytes</td></tr>\n",
  372.        trstring, tdstring, tdstring, doclen);
  373.     printf("<tr %s><th colspan=2 %s>Concurrency Level:</th>"
  374.        "<td colspan=2 %s>%d</td></tr>\n",
  375.        trstring, tdstring, tdstring, concurrency);
  376.     printf("<tr %s><th colspan=2 %s>Time taken for tests:</th>"
  377.        "<td colspan=2 %s>%d.%03d seconds</td></tr>\n",
  378.        trstring, tdstring, tdstring, timetaken / 1000, timetaken % 1000);
  379.     printf("<tr %s><th colspan=2 %s>Complete requests:</th>"
  380.        "<td colspan=2 %s>%d</td></tr>\n",
  381.        trstring, tdstring, tdstring, done);
  382.     printf("<tr %s><th colspan=2 %s>Failed requests:</th>"
  383.        "<td colspan=2 %s>%d</td></tr>\n",
  384.        trstring, tdstring, tdstring, bad);
  385.     if (bad)
  386.     printf("<tr %s><td colspan=4 %s >   (Connect: %d, Length: %d, Exceptions: %d)</td></tr>\n",
  387.            trstring, tdstring, err_conn, err_length, err_except);
  388.     if (err_response)
  389.     printf("<tr %s><th colspan=2 %s>Non-2xx responses:</th>"
  390.            "<td colspan=2 %s>%d</td></tr>\n",
  391.            trstring, tdstring, tdstring, err_response);
  392.     if (keepalive)
  393.     printf("<tr %s><th colspan=2 %s>Keep-Alive requests:</th>"
  394.            "<td colspan=2 %s>%d</td></tr>\n",
  395.            trstring, tdstring, tdstring, doneka);
  396.     printf("<tr %s><th colspan=2 %s>Total transferred:</th>"
  397.        "<td colspan=2 %s>%d bytes</td></tr>\n",
  398.        trstring, tdstring, tdstring, totalread);
  399.     if (posting)
  400.     printf("<tr %s><th colspan=2 %s>Total POSTed:</th>"
  401.            "<td colspan=2 %s>%d</td></tr>\n",
  402.            trstring, tdstring, tdstring, totalposted);
  403.     printf("<tr %s><th colspan=2 %s>HTML transferred:</th>"
  404.        "<td colspan=2 %s>%d bytes</td></tr>\n",
  405.        trstring, tdstring, tdstring, totalbread);
  406.  
  407.     /* avoid divide by zero */
  408.     if (timetaken) {
  409.     printf("<tr %s><th colspan=2 %s>Requests per second:</th>"
  410.            "<td colspan=2 %s>%.2f</td></tr>\n",
  411.        trstring, tdstring, tdstring, 1000 * (float) (done) / timetaken);
  412.     printf("<tr %s><th colspan=2 %s>Transfer rate:</th>"
  413.            "<td colspan=2 %s>%.2f kb/s received</td></tr>\n",
  414.          trstring, tdstring, tdstring, (float) (totalread) / timetaken);
  415.     if (posting) {
  416.         printf("<tr %s><td colspan=2 %s> </td>"
  417.            "<td colspan=2 %s>%.2f kb/s sent</td></tr>\n",
  418.            trstring, tdstring, tdstring,
  419.            (float) (totalposted) / timetaken);
  420.         printf("<tr %s><td colspan=2 %s> </td>"
  421.            "<td colspan=2 %s>%.2f kb/s total</td></tr>\n",
  422.            trstring, tdstring, tdstring,
  423.            (float) (totalread + totalposted) / timetaken);
  424.     }
  425.     }
  426.  
  427.     {
  428.     /* work out connection times */
  429.     int i;
  430.     int totalcon = 0, total = 0;
  431.     int mincon = 9999999, mintot = 999999;
  432.     int maxcon = 0, maxtot = 0;
  433.  
  434.     for (i = 0; i < requests; i++) {
  435.         struct data s = stats[i];
  436.         mincon = ap_min(mincon, s.ctime);
  437.         mintot = ap_min(mintot, s.time);
  438.         maxcon = ap_max(maxcon, s.ctime);
  439.         maxtot = ap_max(maxtot, s.time);
  440.         totalcon += s.ctime;
  441.         total += s.time;
  442.     }
  443.  
  444.     printf("<tr %s><th %s colspan=4>Connnection Times (ms)</th></tr>\n",
  445.            trstring, tdstring);
  446.     printf("<tr %s><th %s> </th> <th %s>min</th>   <th %s>avg</th>   <th %s>max</th></tr>\n",
  447.            trstring, tdstring, tdstring, tdstring, tdstring);
  448.     printf("<tr %s><th %s>Connect:</th>"
  449.            "<td %s>%5d</td>"
  450.            "<td %s>%5d</td>"
  451.            "<td %s>%5d</td></tr>\n",
  452.            trstring, tdstring, tdstring, mincon, tdstring, totalcon / requests, tdstring, maxcon);
  453.     printf("<tr %s><th %s>Processing:</th>"
  454.            "<td %s>%5d</td>"
  455.            "<td %s>%5d</td>"
  456.            "<td %s>%5d</td></tr>\n",
  457.            trstring, tdstring, tdstring, mintot - mincon, tdstring,
  458.            (total / requests) - (totalcon / requests), tdstring, maxtot - maxcon);
  459.     printf("<tr %s><th %s>Total:</th>"
  460.            "<td %s>%5d</td>"
  461.            "<td %s>%5d</td>"
  462.            "<td %s>%5d</td></tr>\n",
  463.            trstring, tdstring, tdstring, mintot, tdstring, total / requests, tdstring, maxtot);
  464.     printf("</table>\n");
  465.     }
  466. }
  467.  
  468. /* --------------------------------------------------------- */
  469.  
  470. /* start asnchronous non-blocking connection */
  471.  
  472. static void start_connect(struct connection *c)
  473. {
  474.     c->read = 0;
  475.     c->bread = 0;
  476.     c->keepalive = 0;
  477.     c->cbx = 0;
  478.     c->gotheader = 0;
  479.  
  480.     c->fd = socket(AF_INET, SOCK_STREAM, 0);
  481.     if (c->fd < 0)
  482.     err("socket");
  483.  
  484.     nonblock(c->fd);
  485.     gettimeofday(&c->start, 0);
  486.  
  487.     if (connect(c->fd, (struct sockaddr *) &server, sizeof(server)) < 0) {
  488.     if (errno == EINPROGRESS) {
  489.         c->state = STATE_CONNECTING;
  490.         FD_SET(c->fd, &writebits);
  491.         return;
  492.     }
  493.     else {
  494.         close(c->fd);
  495.         err_conn++;
  496.         if (bad++ > 10) {
  497.         err("\nTest aborted after 10 failures\n\n");
  498.         }
  499.         start_connect(c);
  500.     }
  501.     }
  502.  
  503.     /* connected first time */
  504.     write_request(c);
  505. }
  506.  
  507. /* --------------------------------------------------------- */
  508.  
  509. /* close down connection and save stats */
  510.  
  511. static void close_connection(struct connection *c)
  512. {
  513.     if (c->read == 0 && c->keepalive) {
  514.     /* server has legitimately shut down an idle keep alive request */
  515.     good--;            /* connection never happend */
  516.     }
  517.     else {
  518.     if (good == 1) {
  519.         /* first time here */
  520.         doclen = c->bread;
  521.     }
  522.     else if (c->bread != doclen) {
  523.         bad++;
  524.         err_length++;
  525.     }
  526.  
  527.     /* save out time */
  528.     if (done < requests) {
  529.         struct data s;
  530.         gettimeofday(&c->done, 0);
  531.         s.read = c->read;
  532.         s.ctime = timedif(c->connect, c->start);
  533.         s.time = timedif(c->done, c->start);
  534.         stats[done++] = s;
  535.     }
  536.     }
  537.  
  538.     close(c->fd);
  539.     FD_CLR(c->fd, &readbits);
  540.     FD_CLR(c->fd, &writebits);
  541.  
  542.     /* connect again */
  543.     start_connect(c);
  544.     return;
  545. }
  546.  
  547. /* --------------------------------------------------------- */
  548.  
  549. /* read data from connection */
  550.  
  551. static void read_connection(struct connection *c)
  552. {
  553.     int r;
  554.     char *part;
  555.     char respcode[4];        /* 3 digits and null */
  556.  
  557.     r = read(c->fd, buffer, sizeof(buffer));
  558.     if (r == 0 || (r < 0 && errno != EAGAIN)) {
  559.     good++;
  560.     close_connection(c);
  561.     return;
  562.     }
  563.  
  564.     if (r < 0 && errno == EAGAIN)
  565.     return;
  566.  
  567.     c->read += r;
  568.     totalread += r;
  569.  
  570.     if (!c->gotheader) {
  571.     char *s;
  572.     int l = 4;
  573.     int space = CBUFFSIZE - c->cbx - 1;    /* -1 to allow for 0 terminator */
  574.     int tocopy = (space < r) ? space : r;
  575. #ifndef CHARSET_EBCDIC
  576.     memcpy(c->cbuff + c->cbx, buffer, space);
  577. #else /*CHARSET_EBCDIC */
  578.     ascii2ebcdic(c->cbuff + c->cbx, buffer, space);
  579. #endif /*CHARSET_EBCDIC */
  580.     c->cbx += tocopy;
  581.     space -= tocopy;
  582.     c->cbuff[c->cbx] = 0;    /* terminate for benefit of strstr */
  583.     if (verbosity >= 4) {
  584.         printf("LOG: header received:\n%s\n", c->cbuff);
  585.     }
  586.     s = strstr(c->cbuff, "\r\n\r\n");
  587.     /* this next line is so that we talk to NCSA 1.5 which blatantly breaks
  588.        the http specifaction */
  589.     if (!s) {
  590.         s = strstr(c->cbuff, "\n\n");
  591.         l = 2;
  592.     }
  593.  
  594.     if (!s) {
  595.         /* read rest next time */
  596.         if (space)
  597.         return;
  598.         else {
  599.         /* header is in invalid or too big - close connection */
  600.         close(c->fd);
  601.         if (bad++ > 10) {
  602.             err("\nTest aborted after 10 failures\n\n");
  603.         }
  604.         FD_CLR(c->fd, &writebits);
  605.         start_connect(c);
  606.         }
  607.     }
  608.     else {
  609.         /* have full header */
  610.         if (!good) {
  611.         /* this is first time, extract some interesting info */
  612.         char *p, *q;
  613.         p = strstr(c->cbuff, "Server:");
  614.         q = servername;
  615.         if (p) {
  616.             p += 8;
  617.             while (*p > 32)
  618.             *q++ = *p++;
  619.         }
  620.         *q = 0;
  621.         }
  622.  
  623.         /* XXX: this parsing isn't even remotely HTTP compliant...
  624.          * but in the interest of speed it doesn't totally have to be,
  625.          * it just needs to be extended to handle whatever servers
  626.          * folks want to test against. -djg */
  627.  
  628.         /* check response code */
  629.         part = strstr(c->cbuff, "HTTP");    /* really HTTP/1.x_ */
  630.         strncpy(respcode, (part + strlen("HTTP/1.x_")), 3);
  631.         respcode[3] = '\0';
  632.         if (respcode[0] != '2') {
  633.         err_response++;
  634.         if (verbosity >= 2)
  635.             printf("WARNING: Response code not 2xx (%s)\n", respcode);
  636.         }
  637.         else if (verbosity >= 3) {
  638.         printf("LOG: Response code = %s\n", respcode);
  639.         }
  640.  
  641.         c->gotheader = 1;
  642.         *s = 0;        /* terminate at end of header */
  643.         if (keepalive &&
  644.         (strstr(c->cbuff, "Keep-Alive")
  645.          || strstr(c->cbuff, "keep-alive"))) {    /* for benefit of MSIIS */
  646.         char *cl;
  647.         cl = strstr(c->cbuff, "Content-Length:");
  648.         /* handle NCSA, which sends Content-length: */
  649.         if (!cl)
  650.             cl = strstr(c->cbuff, "Content-length:");
  651.         if (cl) {
  652.             c->keepalive = 1;
  653.             c->length = atoi(cl + 16);
  654.         }
  655.         }
  656.         c->bread += c->cbx - (s + l - c->cbuff) + r - tocopy;
  657.         totalbread += c->bread;
  658.     }
  659.     }
  660.     else {
  661.     /* outside header, everything we have read is entity body */
  662.     c->bread += r;
  663.     totalbread += r;
  664.     }
  665.  
  666.     if (c->keepalive && (c->bread >= c->length)) {
  667.     /* finished a keep-alive connection */
  668.     good++;
  669.     doneka++;
  670.     /* save out time */
  671.     if (good == 1) {
  672.         /* first time here */
  673.         doclen = c->bread;
  674.     }
  675.     else if (c->bread != doclen) {
  676.         bad++;
  677.         err_length++;
  678.     }
  679.     if (done < requests) {
  680.         struct data s;
  681.         gettimeofday(&c->done, 0);
  682.         s.read = c->read;
  683.         s.ctime = timedif(c->connect, c->start);
  684.         s.time = timedif(c->done, c->start);
  685.         stats[done++] = s;
  686.     }
  687.     c->keepalive = 0;
  688.     c->length = 0;
  689.     c->gotheader = 0;
  690.     c->cbx = 0;
  691.     c->read = c->bread = 0;
  692.     write_request(c);
  693.     c->start = c->connect;    /* zero connect time with keep-alive */
  694.     }
  695. }
  696.  
  697. /* --------------------------------------------------------- */
  698.  
  699. /* run the tests */
  700.  
  701. static void test(void)
  702. {
  703.     struct timeval timeout, now;
  704.     fd_set sel_read, sel_except, sel_write;
  705.     int i;
  706.  
  707.     if (!use_html) {
  708.     printf("Benchmarking %s (be patient)...", hostname);
  709.     fflush(stdout);
  710.     }
  711.  
  712.     {
  713.     /* get server information */
  714.     struct hostent *he;
  715.     he = gethostbyname(hostname);
  716.     if (!he)
  717.         err("bad hostname");
  718.     server.sin_family = he->h_addrtype;
  719.     server.sin_port = htons(port);
  720.     server.sin_addr.s_addr = ((unsigned long *) (he->h_addr_list[0]))[0];
  721.     }
  722.  
  723.     con = malloc(concurrency * sizeof(struct connection));
  724.     memset(con, 0, concurrency * sizeof(struct connection));
  725.  
  726.     stats = malloc(requests * sizeof(struct data));
  727.  
  728.     FD_ZERO(&readbits);
  729.     FD_ZERO(&writebits);
  730.  
  731.     /* setup request */
  732.     if (!posting) {
  733.     sprintf(request, "GET %s HTTP/1.0\r\n"
  734.         "User-Agent: ApacheBench/%s\r\n"
  735.         "%s"
  736.         "Host: %s\r\n"
  737.         "Accept: */*\r\n"
  738.         "\r\n",
  739.         path,
  740.         VERSION,
  741.         keepalive ? "Connection: Keep-Alive\r\n" : "",
  742.         hostname);
  743.     }
  744.     else {
  745.     sprintf(request, "POST %s HTTP/1.0\r\n"
  746.         "User-Agent: ApacheBench/%s\r\n"
  747.         "%s"
  748.         "Host: %s\r\n"
  749.         "Accept: */*\r\n"
  750.         "Content-length: %d\r\n"
  751.         "Content-type: %s\r\n"
  752.         "\r\n",
  753.         path,
  754.         VERSION,
  755.         keepalive ? "Connection: Keep-Alive\r\n" : "",
  756.         hostname, postlen,
  757.         (content_type[0]) ? content_type : "text/plain");
  758.     }
  759.  
  760.     if (verbosity >= 2)
  761.     printf("INFO: POST header == \n---\n%s\n---\n", request);
  762.  
  763.     reqlen = strlen(request);
  764.  
  765. #ifdef CHARSET_EBCDIC
  766.     ebcdic2ascii(request, request, reqlen);
  767. #endif /*CHARSET_EBCDIC */
  768.  
  769.     /* ok - lets start */
  770.     gettimeofday(&start, 0);
  771.  
  772.     /* initialise lots of requests */
  773.     for (i = 0; i < concurrency; i++)
  774.     start_connect(&con[i]);
  775.  
  776.     while (done < requests) {
  777.     int n;
  778.     /* setup bit arrays */
  779.     memcpy(&sel_except, &readbits, sizeof(readbits));
  780.     memcpy(&sel_read, &readbits, sizeof(readbits));
  781.     memcpy(&sel_write, &writebits, sizeof(readbits));
  782.  
  783.     /* check for time limit expiry */
  784.     gettimeofday(&now, 0);
  785.     if (tlimit && timedif(now, start) > (tlimit * 1000)) {
  786.         requests = done;    /* so stats are correct */
  787.     }
  788.  
  789.     /* Timeout of 30 seconds. */
  790.     timeout.tv_sec = 30;
  791.     timeout.tv_usec = 0;
  792.     n = ap_select(FD_SETSIZE, &sel_read, &sel_write, &sel_except, &timeout);
  793.     if (!n) {
  794.         err("\nServer timed out\n\n");
  795.     }
  796.     if (n < 1)
  797.         err("select");
  798.  
  799.     for (i = 0; i < concurrency; i++) {
  800.         int s = con[i].fd;
  801.         if (FD_ISSET(s, &sel_except)) {
  802.         bad++;
  803.         err_except++;
  804.         start_connect(&con[i]);
  805.         continue;
  806.         }
  807.         if (FD_ISSET(s, &sel_read))
  808.         read_connection(&con[i]);
  809.         if (FD_ISSET(s, &sel_write))
  810.         write_request(&con[i]);
  811.     }
  812.     }
  813.     if (use_html)
  814.     output_html_results();
  815.     else
  816.     output_results();
  817. }
  818.  
  819. /* ------------------------------------------------------- */
  820.  
  821. /* display copyright information */
  822. static void copyright(void)
  823. {
  824.     if (!use_html) {
  825.     printf("This is ApacheBench, Version %s\n", VERSION);
  826.     printf("Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/\n");
  827.     printf("Copyright (c) 1998-1999 The Apache Group, http://www.apache.org/\n");
  828.     printf("\n");
  829.     }
  830.     else {
  831.     printf("<p>\n");
  832.     printf(" This is ApacheBench, Version %s<br>\n", VERSION);
  833.     printf(" Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/<br>\n");
  834.     printf(" Copyright (c) 1998-1999 The Apache Group, http://www.apache.org/<br>\n");
  835.     printf("</p>\n<p>\n");
  836.     }
  837. }
  838.  
  839. /* display usage information */
  840. static void usage(char *progname)
  841. {
  842.     fprintf(stderr, "Usage: %s [options] [http://]hostname[:port]/path\n", progname);
  843.     fprintf(stderr, "Options are:\n");
  844.     fprintf(stderr, "    -n requests     Number of requests to perform\n");
  845.     fprintf(stderr, "    -c concurrency  Number of multiple requests to make\n");
  846.     fprintf(stderr, "    -t timelimit    Seconds to max. wait for responses\n");
  847.     fprintf(stderr, "    -p postfile     File containg data to POST\n");
  848.     fprintf(stderr, "    -T content-type Content-type header for POSTing\n");
  849.     fprintf(stderr, "    -v verbosity    How much troubleshooting info to print\n");
  850.     fprintf(stderr, "    -w              Print out results in HTML tables\n");
  851.     fprintf(stderr, "    -x attributes   String to insert as table attributes\n");
  852.     fprintf(stderr, "    -y attributes   String to insert as tr attributes\n");
  853.     fprintf(stderr, "    -z attributes   String to insert as td or th attributes\n");
  854.     fprintf(stderr, "    -V              Print version number and exit\n");
  855.     fprintf(stderr, "    -k              Use HTTP KeepAlive feature\n");
  856.     fprintf(stderr, "    -h              Display usage information (this message)\n");
  857.     exit(EINVAL);
  858. }
  859.  
  860. /* ------------------------------------------------------- */
  861.  
  862. /* split URL into parts */
  863.  
  864. static int parse_url(char *url)
  865. {
  866.     char *cp;
  867.     char *h;
  868.     char *p = NULL;
  869.  
  870.     if (strlen(url) > 7 && strncmp(url, "http://", 7) == 0)
  871.     url += 7;
  872.     h = url;
  873.     if ((cp = strchr(url, ':')) != NULL) {
  874.     *cp++ = '\0';
  875.     p = cp;
  876.     url = cp;
  877.     }
  878.     if ((cp = strchr(url, '/')) == NULL)
  879.     return 1;
  880.     strcpy(path, cp);
  881.     *cp = '\0';
  882.     strcpy(hostname, h);
  883.     if (p != NULL)
  884.     port = atoi(p);
  885.     return 0;
  886. }
  887.  
  888. /* ------------------------------------------------------- */
  889.  
  890. /* read data to POST from file, save contents and length */
  891.  
  892. static int open_postfile(char *pfile)
  893. {
  894.     int postfd, status;
  895.     struct stat postfilestat;
  896.  
  897.     if ((postfd = open(pfile, O_RDONLY)) == -1) {
  898.     printf("Invalid postfile name (%s)\n", pfile);
  899.     return errno;
  900.     }
  901.     if ((status = fstat(postfd, &postfilestat)) == -1) {
  902.     perror("Can\'t stat postfile\n");
  903.     return status;
  904.     }
  905.     postdata = malloc(postfilestat.st_size);
  906.     if (!postdata) {
  907.     printf("Can\'t alloc postfile buffer\n");
  908.     return ENOMEM;
  909.     }
  910.     if (read(postfd, postdata, postfilestat.st_size) != postfilestat.st_size) {
  911.     printf("error reading postfilen");
  912.     return EIO;
  913.     }
  914.     postlen = postfilestat.st_size;
  915.     return 0;
  916. }
  917.  
  918. /* ------------------------------------------------------- */
  919.  
  920. extern char *optarg;
  921. extern int optind, opterr, optopt;
  922.  
  923. /* sort out command-line args and call test */
  924. int main(int argc, char **argv)
  925. {
  926.     int c, r;
  927.  
  928.     /* table defaults  */
  929.     tablestring = "";
  930.     trstring = "";
  931.     tdstring = "bgcolor=white";
  932.  
  933.     optind = 1;
  934.     while ((c = getopt(argc, argv, "n:c:t:T:p:v:kVhwx:y:z:")) > 0) {
  935.     switch (c) {
  936.     case 'n':
  937.         requests = atoi(optarg);
  938.         if (!requests) {
  939.         err("Invalid number of requests\n");
  940.         }
  941.         break;
  942.     case 'k':
  943.         keepalive = 1;
  944.         break;
  945.     case 'c':
  946.         concurrency = atoi(optarg);
  947.         break;
  948.     case 'p':
  949.         if (0 == (r = open_postfile(optarg))) {
  950.         posting = 1;
  951.         }
  952.         else if (postdata) {
  953.         exit(r);
  954.         }
  955.         break;
  956.     case 'v':
  957.         verbosity = atoi(optarg);
  958.         break;
  959.     case 't':
  960.         tlimit = atoi(optarg);
  961.         requests = MAX_REQUESTS;    /* need to size data array on something */
  962.         break;
  963.     case 'T':
  964.         strcpy(content_type, optarg);
  965.         break;
  966.     case 'V':
  967.         copyright();
  968.         exit(0);
  969.         break;
  970.     case 'w':
  971.         use_html = 1;
  972.         break;
  973.         /* if any of the following three are used, turn on html output automatically  */
  974.     case 'x':
  975.         use_html = 1;
  976.         tablestring = optarg;
  977.         break;
  978.     case 'y':
  979.         use_html = 1;
  980.         trstring = optarg;
  981.         break;
  982.     case 'z':
  983.         use_html = 1;
  984.         tdstring = optarg;
  985.         break;
  986.     case 'h':
  987.         usage(argv[0]);
  988.         break;
  989.     default:
  990.         fprintf(stderr, "%s: invalid option `%c'\n", argv[0], c);
  991.         usage(argv[0]);
  992.         break;
  993.     }
  994.     }
  995.     if (optind != argc - 1) {
  996.     fprintf(stderr, "%s: wrong number of arguments\n", argv[0]);
  997.     usage(argv[0]);
  998.     }
  999.  
  1000.     if (parse_url(argv[optind++])) {
  1001.     fprintf(stderr, "%s: invalid URL\n", argv[0]);
  1002.     usage(argv[0]);
  1003.     }
  1004.  
  1005.     copyright();
  1006.     test();
  1007.  
  1008.     exit(0);
  1009. }
  1010.